﻿#region Using Statements
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.ComponentModel;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using QuickGraph;
using System.Xml.Serialization;
#endregion

namespace SharedCongkak
{
    [System.Xml.Serialization.XmlInclude(typeof(Hole))]
    public abstract class AbstractHole: INotifyPropertyChanged
    {
        #region fields
        protected Vector2 position;

        protected int width = 100;
        protected int height = 100;
        protected float radius = 40;
        protected Color circleColor = new Color(150, 150, 150, 255);

        protected Texture2D holeTex;
        protected Texture2D selectedHoleTex;

        protected List<Path> nextEdge = new List<Path>();
        protected List<Path> prevEdge = new List<Path>();
        protected List<Seed> seeds = new List<Seed>();

        protected Rectangle collisionRect;
        protected static Random random = new Random();
        protected String name;
        #endregion

        public event EventHandler OnSelect;
        public event EventHandler OnEnter;
        public event EventHandler OnLeave;
        public event EventHandler<HoleEventArgs> OnSeedAdded;
        public event EventHandler OnPositionChanged;


        [ContentSerializerIgnore]
        [XmlIgnore()]
        [Browsable(false)]
        public List<Path> NextEdges
        {
            get { return nextEdge; }
        }
        [ContentSerializerIgnore]
        [XmlIgnore()]
        [Browsable(false)]
        public List<Path> PrevEdges
        {
            get { return prevEdge; }
        }

        [XmlAttribute("positionX")]
        [ContentSerializerIgnore]
        [XmlIgnore()]
        protected float PositionX
        {
            get { return position.X; }
            set
            {
                position.X = value;
                collisionRect.X = (int)position.X;
            }
        }

        [XmlAttribute("positionY")]
        [ContentSerializerIgnore]
        [XmlIgnore()]
        protected float PositionY
        {
            get { return position.Y; }
            set
            {
                position.Y = value;
                collisionRect.Y = (int)position.Y;
            }
        }
        [ContentSerializerIgnore]
        [XmlIgnore()]
        [Browsable(false)]
        public float Radius
        {
            set { radius = value; }
            get { return radius; }
        }
        [ContentSerializerIgnore]
        [XmlIgnore()]
        [Browsable(false)]
        public Rectangle CollisionRect
        {
            get { return collisionRect; }
            protected set { collisionRect = value; }
        }

        //[Browsable(false)]
        //[ReadOnlyAttribute(true)]
        [DescriptionAttribute("Top-left location of the hole")]
        public Vector2 Position
        {
            get { return position; }
            set
            {
                PositionX = value.X;
                PositionY = value.Y;
                if (OnPositionChanged != null)
                    OnPositionChanged(this, null);
                NotifyPropertyChanged("Position");
            }
        }
        [DescriptionAttribute("Name of the hole.")]
        public String Name
        {
            get { return name; }
            set { name = value; NotifyPropertyChanged("Name"); }
        }

        [DescriptionAttribute("IsHome property decides whether the hole user-selectable.")]
        public bool IsHome
        {
            get;
            set;
        }

        [ContentSerializerIgnore]
        //[XmlIgnore()]
        [DescriptionAttribute("Center location of the hole")]
        public Vector2 Center
        {
            get
            {
                return Position + new Vector2(width / 2.0f, height / 2.0f);
            }
            set
            {
                Position = value - new Vector2(width / 2.0f, height / 2.0f);
            }
        }

        //[Browsable(false)]
        public int NoOfSeeds
        {
            get
            {
                return seeds.Count;
            }
        }

        [ContentSerializerIgnore]
        [XmlIgnore()]
        [Browsable(false)]
        public bool Highlight
        {
            set;
            get;
        }

        public override string ToString()
        {
            return Name;
        }

        public virtual void LoadContent(ContentManager content)
        {

        }

        public virtual void Draw(SpriteBatch spriteBatch)
        {
            foreach (var seed in seeds)
            {
                seed.Draw(spriteBatch, Center);
            }
        }


        public virtual bool MouseHover(int X, int Y)
        {
            if (!ContainsPoint(new Point(X, Y)))
                return false;
            if (OnEnter != null)
                OnEnter(this, null);
            return true;
        }

        public virtual void MouseClicked(int X, int Y)
        {
            if (OnSelect == null) return;

            if(ContainsPoint(new Point(X, Y)))
                OnSelect(this, null);
        }


        public virtual bool ContainsPoint(Point point)
        {
            if (collisionRect.Contains(point))
            {
                Color[] pixelColor = new Color[1];
                Point texPos = new Point(
                    point.X - collisionRect.Location.X,
                    point.Y - collisionRect.Location.Y
                    );
                holeTex.GetData<Color>(0, new Rectangle(texPos.X, texPos.Y, 1, 1), pixelColor, 0, 1);
                if (pixelColor[0] == circleColor)
                    return true;
            }
            return false;
        }


        public virtual void AddSeed(Seed seed)
        {
            if (seeds.Contains(seed))
                return;
            float maxXOffset = (radius - 7) - seed.Width / 2.0f;
            float maxYOffset = (radius - 7) - seed.Height / 2.0f;
            float xOffset = MathHelper.Lerp((float)-maxXOffset, (float)maxXOffset, (float)random.NextDouble());
            float yOffset = MathHelper.Lerp((float)-maxYOffset, (float)maxYOffset, (float)random.NextDouble());
            seed.Center = new Vector2(xOffset, yOffset);
            seeds.Add(seed);
            if (OnSeedAdded != null)
                OnSeedAdded(this, new HoleEventArgs(seed));

        }

        public List<Seed> GetAndRemoveAllSeeds()
        {
            List<Seed> newList = new List<Seed>(seeds);
            seeds.Clear();
            return newList;
        }

        public Seed GetAndRemoveSeed()
        {
            Seed seed = seeds[seeds.Count - 1];
            seeds.Remove(seed);
            return seed;
        }

        public void StartPathEffect()
        {
            foreach (var path in NextEdges)
            {
                path.StartEffect();
            }
        }

        public static Hole CreateHole(ContentManager content)
        {
            Hole newHole = new Hole();
            newHole.LoadContent(content);
            return newHole;
        }

        public static Store CreateStore(ContentManager content)
        {
            Store newStore = new Store();
            newStore.LoadContent(content);
            return newStore;
        }

        private void NotifyPropertyChanged(String propertyName)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    public class AbstractHoleReader : ContentTypeReader<AbstractHole>
    {
        protected override AbstractHole Read(
                ContentReader input,
                AbstractHole existingInstance)
        {
            AbstractHole abstractHole;
            Type type = input.GetType();
            Vector2 position = input.ReadVector2();
            if (type == typeof(Store))
                abstractHole = new Store(input.ReadString());
            else
                abstractHole = new Hole(input.ReadString());

            abstractHole.Position = position;
            abstractHole.IsHome = input.ReadBoolean();
            abstractHole.LoadContent(input.ContentManager);
            return abstractHole;
        }
    }

}
